/*++

Copyright (c) 1996    Microsoft Corporation

Module Name:

    pnp.c

Abstract:

    This module contains the code
    for finding, adding, removing, and identifying hid devices.

Environment:

    User mode

--*/

#include "StdAfx.h"
#include <dbt.h>
#include "hid.h"
#include <basetyps.h>
#include <stdlib.h>
#include <wtypes.h>
#include <setupapi.h>

#define TENX_VID 0x1130
#define TENX_PID 0x0202

/*++
Routine Description:
   Do the required PnP things in order to find the 1603 HID device in
   the system at this time.
--*/

BOOLEAN FindGenericHIDDevice(PHID_DEVICE HidDevice, PHID_DEVICE HidCBWDevice, HWND hWnd) 
{
    HDEVINFO                            hardwareDeviceInfo;
    SP_INTERFACE_DEVICE_DATA            deviceInfoData;
    ULONG                               i;
    GUID                                hidGuid;
    PSP_INTERFACE_DEVICE_DETAIL_DATA    functionClassDeviceData = NULL;
    ULONG                               predictedLength = 0;
    ULONG                               requiredLength = 0;
	BOOL	bFindDevice, bFindCBWDevice, bFind; 
	
	bFindDevice = bFindCBWDevice = FALSE;


    HidD_GetHidGuid (&hidGuid);

    //
    // Open a handle to the plug and play dev node.
    //
    hardwareDeviceInfo = SetupDiGetClassDevs ( &hidGuid,
                                               NULL, // Define no enumerator (global)
                                               NULL, // Define no
                                               (DIGCF_PRESENT | // Only Devices present
                                                DIGCF_DEVICEINTERFACE)); // Function class devices.


    deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);


    for (i=0; SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 0, &hidGuid, i, &deviceInfoData); i++) {
		bFind = FALSE;

		//
        // allocate a function class device data structure to receive the
        // goods about this particular device.
        //

        SetupDiGetDeviceInterfaceDetail (
                hardwareDeviceInfo,
                &deviceInfoData,
                NULL, // probing so no output buffer yet
                0, // probing so output buffer length of zero
                &requiredLength,
                NULL); // not interested in the specific dev-node


        predictedLength = requiredLength;

        functionClassDeviceData = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc (predictedLength);
        if (functionClassDeviceData)
        {
            functionClassDeviceData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);
        }
        else
        {
            SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
            return FALSE;
        }

        //
        // Retrieve the information from Plug and Play.
        //

        if (! SetupDiGetDeviceInterfaceDetail (
                   hardwareDeviceInfo,
                   &deviceInfoData,
                   functionClassDeviceData,
                   predictedLength,
                   &requiredLength,
                   NULL)) 
        {
            SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
            return FALSE;
        }

        //
        // Open device with just generic query abilities to begin with
        //
              
		if(!bFindDevice) {
			bFind = bFindDevice = OpenHidDevice (functionClassDeviceData -> DevicePath, 
						   TRUE,      // ReadAccess - none
						   TRUE,      // WriteAccess - none
						   FALSE,       // Overlapped - no
						   FALSE,       // Exclusive - no
						   TRUE,        // GetDeviceInfo - yes
						   HidDevice,
						   hWnd,
						   1, 0);
		} 

		if(!bFindCBWDevice && !bFind) {
			bFindCBWDevice = OpenHidDevice (functionClassDeviceData -> DevicePath, 
						   TRUE,      // ReadAccess - none
						   TRUE,      // WriteAccess - none
						   FALSE,       // Overlapped - no
						   FALSE,       // Exclusive - no
						   TRUE,        // GetDeviceInfo - yes
						   HidCBWDevice,
						   hWnd,
						   1, 3);
		}

		if(bFindDevice && bFindCBWDevice) break;
	}

    SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
    return (bFindDevice && bFindCBWDevice);
}


/*++
RoutineDescription:
    Given the HardwareDeviceInfo, representing a handle to the plug and
    play information, and deviceInfoData, representing a specific hid device,
    open that device and fill in all the relivant information in the given
    HID_DEVICE structure.

    return if the open and initialization was successfull or not.

--*/
BOOLEAN
OpenHidDevice (
    IN       PCHAR          DevicePath,
    IN       BOOL           HasReadAccess,
    IN       BOOL           HasWriteAccess,
    IN       BOOL           IsOverlapped,
    IN       BOOL           IsExclusive,
    IN       BOOL           GetDeviceInfo,
    IN OUT   PHID_DEVICE    HidDevice,
	IN		 HWND			hWnd,
	IN		 USHORT			usUsagePage,
	IN		 USHORT			usUsageID
)
{
    DWORD   accessFlags = 0;
    DWORD   sharingFlags = 0;
    
	DWORD dwLen = strlen(DevicePath);
    HidDevice -> DevicePath = (PCHAR)malloc(dwLen + 1);

    if (NULL == HidDevice -> DevicePath) 
    {
        return (FALSE);
    }

    strcpy(HidDevice -> DevicePath, DevicePath);
	HidDevice -> DevicePath[dwLen] = '\0';
    
    if (HasReadAccess)
    {
        accessFlags |= GENERIC_READ;
    }

    if (HasWriteAccess)
    {
        accessFlags |= GENERIC_WRITE;
    }

    if (!IsExclusive)
    {
        sharingFlags = FILE_SHARE_READ | FILE_SHARE_WRITE;
    }
    
    HidDevice->HidDevice = CreateFile (DevicePath,
                                       accessFlags,
                                       sharingFlags,
                                       NULL,        // no SECURITY_ATTRIBUTES structure
                                       OPEN_EXISTING, // No special create flags
                                       IsOverlapped ? FILE_FLAG_OVERLAPPED : 0, 
                                       NULL);       // No template file

    if (INVALID_HANDLE_VALUE == HidDevice->HidDevice) 
    {
        free(HidDevice -> DevicePath);
        return FALSE;
    }

    HidDevice -> OpenedForRead = HasReadAccess;
    HidDevice -> OpenedForWrite = HasWriteAccess;
    HidDevice -> OpenedOverlapped = IsOverlapped;
    HidDevice -> OpenedExclusive = IsExclusive;
    
    //
    // If the device was not opened as overlapped, then fill in the rest of the
    //  HidDevice structure.  However, if opened as overlapped, this handle cannot
    //  be used in the calls to the HidD_ exported functions since each of these
    //  functions does synchronous I/O.
    //
 
    if (!HidD_GetAttributes (HidDevice->HidDevice, &HidDevice->Attributes)) 
    {
        free(HidDevice -> DevicePath);
        CloseHandle(HidDevice -> HidDevice);
        return FALSE;
    } else {	// Check the VID/PID
		if( (HidDevice->Attributes.VendorID != TENX_VID) || (HidDevice->Attributes.ProductID != TENX_PID) ) {
			free(HidDevice -> DevicePath);
			CloseHandle(HidDevice -> HidDevice);
			return FALSE;			
		}
	}

	
    if (GetDeviceInfo) 
    {
        if (!HidD_GetPreparsedData (HidDevice->HidDevice, &HidDevice->Ppd)) 
        {
            free(HidDevice -> DevicePath);
            CloseHandle(HidDevice -> HidDevice);
            return FALSE;
        }

        if (!HidP_GetCaps (HidDevice->Ppd, &HidDevice->Caps))
        {
            free(HidDevice -> DevicePath);

            CloseHandle(HidDevice -> HidDevice);

            HidD_FreePreparsedData (HidDevice->Ppd);

            return FALSE;
        } else {	// Check the usage page and usage ID.
			if((HidDevice->Caps.UsagePage != (USAGE)usUsagePage) || (HidDevice->Caps.Usage != (USAGE)usUsageID)) {
				free(HidDevice -> DevicePath);

				CloseHandle(HidDevice -> HidDevice);

				HidD_FreePreparsedData (HidDevice->Ppd);

				return FALSE;
			}
		}


        //
        // At this point the client has a choice.  It may chose to look at the
        // Usage and Page of the top level collection found in the HIDP_CAPS
        // structure.  In this way it could just use the usages it knows about.
        // If either HidP_GetUsages or HidP_GetUsageValue return an error then
        // that particular usage does not exist in the report.
        // This is most likely the preferred method as the application can only
        // use usages of which it already knows.
        // In this case the app need not even call GetButtonCaps or GetValueCaps.
        //
        // In this example, however, we will call FillDeviceInfo to look for all
        //    of the usages in the device.
        //

        FillDeviceInfo(HidDevice);

		DEV_BROADCAST_HANDLE broadcastHandle;

		broadcastHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
		broadcastHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
		broadcastHandle.dbch_handle = HidDevice->HidDevice;

		HidDevice->hDeviceNotificationHandle = RegisterDeviceNotification( 
									hWnd,
									&broadcastHandle,
									DEVICE_NOTIFY_WINDOW_HANDLE);
		HidDevice->bFound = TRUE;
    }
    
    return (TRUE);
}

VOID
FillDeviceInfo(
    IN  PHID_DEVICE HidDevice
)
{
    USHORT              numValues;
    USHORT              numCaps;
    PHIDP_BUTTON_CAPS   buttonCaps;
    PHIDP_VALUE_CAPS    valueCaps;
    PHID_DATA           data;
    ULONG               i;
    USAGE               usage;

    //
    // setup Input Data buffers.
    //

    //
    // Allocate memory to hold on input report
    //

    HidDevice->InputReportBuffer = (PCHAR) 
        calloc (HidDevice->Caps.InputReportByteLength, sizeof (CHAR));


    //
    // Allocate memory to hold the button and value capabilities.
    // NumberXXCaps is in terms of array elements.
    //
    
    HidDevice->InputButtonCaps = buttonCaps = (PHIDP_BUTTON_CAPS)
        calloc (HidDevice->Caps.NumberInputButtonCaps, sizeof (HIDP_BUTTON_CAPS));

    HidDevice->InputValueCaps = valueCaps = (PHIDP_VALUE_CAPS)
        calloc (HidDevice->Caps.NumberInputValueCaps, sizeof (HIDP_VALUE_CAPS));

    //
    // Have the HidP_X functions fill in the capability structure arrays.
    //

    numCaps = HidDevice->Caps.NumberInputButtonCaps;

    HidP_GetButtonCaps (HidP_Input,
                        buttonCaps,
                        &numCaps,
                        HidDevice->Ppd);

    numCaps = HidDevice->Caps.NumberInputValueCaps;

    HidP_GetValueCaps (HidP_Input,
                       valueCaps,
                       &numCaps,
                       HidDevice->Ppd);


    //
    // Depending on the device, some value caps structures may represent more
    // than one value.  (A range).  In the interest of being verbose, over
    // efficient, we will expand these so that we have one and only one
    // struct _HID_DATA for each value.
    //
    // To do this we need to count up the total number of values are listed
    // in the value caps structure.  For each element in the array we test
    // for range if it is a range then UsageMax and UsageMin describe the
    // usages for this range INCLUSIVE.
    //
    
    numValues = 0;
    for (i = 0; i < HidDevice->Caps.NumberInputValueCaps; i++, valueCaps++) 
    {
        if (valueCaps->IsRange) 
        {
            numValues += valueCaps->Range.UsageMax - valueCaps->Range.UsageMin + 1;
        }
        else
        {
            numValues++;
        }
    }
    valueCaps = HidDevice->InputValueCaps;


    //
    // Allocate a buffer to hold the struct _HID_DATA structures.
    // One element for each set of buttons, and one element for each value
    // found.
    //

    HidDevice->InputDataLength = HidDevice->Caps.NumberInputButtonCaps
                               + numValues;

    HidDevice->InputData = data = (PHID_DATA)
        calloc (HidDevice->InputDataLength, sizeof (HID_DATA));

    //
    // Fill in the button data
    //

    for (i = 0;
         i < HidDevice->Caps.NumberInputButtonCaps;
         i++, data++, buttonCaps++) 
    {
        data->IsButtonData = TRUE;
        data->Status = HIDP_STATUS_SUCCESS;
        data->UsagePage = buttonCaps->UsagePage;
        if (buttonCaps->IsRange) 
        {
            data->ButtonData.UsageMin = buttonCaps -> Range.UsageMin;
            data->ButtonData.UsageMax = buttonCaps -> Range.UsageMax;
        }
        else
        {
            data -> ButtonData.UsageMin = data -> ButtonData.UsageMax = buttonCaps -> NotRange.Usage;
        }
        
        data->ButtonData.MaxUsageLength = HidP_MaxUsageListLength (
                                                HidP_Input,
                                                buttonCaps->UsagePage,
                                                HidDevice->Ppd);
        data->ButtonData.Usages = (PUSAGE)
            calloc (data->ButtonData.MaxUsageLength, sizeof (USAGE));

        data->ReportID = buttonCaps -> ReportID;
    }

    //
    // Fill in the value data
    //

    for (i = 0; i < numValues; i++, valueCaps++)
    {
        if (valueCaps->IsRange) 
        {
            for (usage = valueCaps->Range.UsageMin;
                 usage <= valueCaps->Range.UsageMax;
                 usage++) 
            {
                data->IsButtonData = FALSE;
                data->Status = HIDP_STATUS_SUCCESS;
                data->UsagePage = valueCaps->UsagePage;
                data->ValueData.Usage = usage;
                data->ReportID = valueCaps -> ReportID;
                data++;
            }
        } 
        else
        {
            data->IsButtonData = FALSE;
            data->Status = HIDP_STATUS_SUCCESS;
            data->UsagePage = valueCaps->UsagePage;
            data->ValueData.Usage = valueCaps->NotRange.Usage;
            data->ReportID = valueCaps -> ReportID;
            data++;
        }
    }

    //
    // setup Output Data buffers.
    //

    HidDevice->OutputReportBuffer = (PCHAR)
        calloc (HidDevice->Caps.OutputReportByteLength, sizeof (CHAR));

    HidDevice->OutputButtonCaps = buttonCaps = (PHIDP_BUTTON_CAPS)
        calloc (HidDevice->Caps.NumberOutputButtonCaps, sizeof (HIDP_BUTTON_CAPS));

    HidDevice->OutputValueCaps = valueCaps = (PHIDP_VALUE_CAPS)
        calloc (HidDevice->Caps.NumberOutputValueCaps, sizeof (HIDP_VALUE_CAPS));

    numCaps = HidDevice->Caps.NumberOutputButtonCaps;
    HidP_GetButtonCaps (HidP_Output,
                        buttonCaps,
                        &numCaps,
                        HidDevice->Ppd);

    numCaps = HidDevice->Caps.NumberOutputValueCaps;
    HidP_GetValueCaps (HidP_Output,
                       valueCaps,
                       &numCaps,
                       HidDevice->Ppd);

    numValues = 0;
    for (i = 0; i < HidDevice->Caps.NumberOutputValueCaps; i++, valueCaps++) 
    {
        if (valueCaps->IsRange) 
        {
            numValues += valueCaps->Range.UsageMax
                       - valueCaps->Range.UsageMin + 1;
        } 
        else
        {
            numValues++;
        }
    }
    valueCaps = HidDevice->OutputValueCaps;

    HidDevice->OutputDataLength = HidDevice->Caps.NumberOutputButtonCaps
                                + numValues;

    HidDevice->OutputData = data = (PHID_DATA)
       calloc (HidDevice->OutputDataLength, sizeof (HID_DATA));

    for (i = 0;
         i < HidDevice->Caps.NumberOutputButtonCaps;
         i++, data++, buttonCaps++) 
    {
        data->IsButtonData = TRUE;
        data->Status = HIDP_STATUS_SUCCESS;
        data->UsagePage = buttonCaps->UsagePage;

        if (buttonCaps->IsRange)
        {
            data->ButtonData.UsageMin = buttonCaps -> Range.UsageMin;
            data->ButtonData.UsageMax = buttonCaps -> Range.UsageMax;
        }
        else
        {
            data -> ButtonData.UsageMin = data -> ButtonData.UsageMax = buttonCaps -> NotRange.Usage;
        }

        data->ButtonData.MaxUsageLength = HidP_MaxUsageListLength (
                                                   HidP_Output,
                                                   buttonCaps->UsagePage,
                                                   HidDevice->Ppd);

        data->ButtonData.Usages = (PUSAGE)
            calloc (data->ButtonData.MaxUsageLength, sizeof (USAGE));

        data->ReportID = buttonCaps -> ReportID;
    }

    for (i = 0; i < numValues; i++, valueCaps++)
    {
        if (valueCaps->IsRange)
        {
            for (usage = valueCaps->Range.UsageMin;
                 usage <= valueCaps->Range.UsageMax;
                 usage++) 
            {
                data->IsButtonData = FALSE;
                data->Status = HIDP_STATUS_SUCCESS;
                data->UsagePage = valueCaps->UsagePage;
                data->ValueData.Usage = usage;
                data->ReportID = valueCaps -> ReportID;
                data++;
            }
        }
        else
        {
            data->IsButtonData = FALSE;
            data->Status = HIDP_STATUS_SUCCESS;
            data->UsagePage = valueCaps->UsagePage;
            data->ValueData.Usage = valueCaps->NotRange.Usage;
            data->ReportID = valueCaps -> ReportID;
            data++;
        }
    }

    //
    // setup Feature Data buffers.
    //

    HidDevice->FeatureReportBuffer = (PCHAR)
           calloc (HidDevice->Caps.FeatureReportByteLength, sizeof (CHAR));

    HidDevice->FeatureButtonCaps = buttonCaps = (PHIDP_BUTTON_CAPS)
        calloc (HidDevice->Caps.NumberFeatureButtonCaps, sizeof (HIDP_BUTTON_CAPS));

    HidDevice->FeatureValueCaps = valueCaps = (PHIDP_VALUE_CAPS)
        calloc (HidDevice->Caps.NumberFeatureValueCaps, sizeof (HIDP_VALUE_CAPS));

    numCaps = HidDevice->Caps.NumberFeatureButtonCaps;
    HidP_GetButtonCaps (HidP_Feature,
                        buttonCaps,
                        &numCaps,
                        HidDevice->Ppd);

    numCaps = HidDevice->Caps.NumberFeatureValueCaps;
    HidP_GetValueCaps (HidP_Feature,
                       valueCaps,
                       &numCaps,
                       HidDevice->Ppd);

    numValues = 0;
    for (i = 0; i < HidDevice->Caps.NumberFeatureValueCaps; i++, valueCaps++) 
    {
        if (valueCaps->IsRange) 
        {
            numValues += valueCaps->Range.UsageMax
                       - valueCaps->Range.UsageMin + 1;
        }
        else
        {
            numValues++;
        }
    }
    valueCaps = HidDevice->FeatureValueCaps;

    HidDevice->FeatureDataLength = HidDevice->Caps.NumberFeatureButtonCaps
                                 + numValues;

    HidDevice->FeatureData = data = (PHID_DATA)
        calloc (HidDevice->FeatureDataLength, sizeof (HID_DATA));

    for (i = 0;
         i < HidDevice->Caps.NumberFeatureButtonCaps;
         i++, data++, buttonCaps++) 
    {
        data->IsButtonData = TRUE;
        data->Status = HIDP_STATUS_SUCCESS;
        data->UsagePage = buttonCaps->UsagePage;

        if (buttonCaps->IsRange)
        {
            data->ButtonData.UsageMin = buttonCaps -> Range.UsageMin;
            data->ButtonData.UsageMax = buttonCaps -> Range.UsageMax;
        }
        else
        {
            data -> ButtonData.UsageMin = data -> ButtonData.UsageMax = buttonCaps -> NotRange.Usage;
        }
        
        data->ButtonData.MaxUsageLength = HidP_MaxUsageListLength (
                                                HidP_Feature,
                                                buttonCaps->UsagePage,
                                                HidDevice->Ppd);
        data->ButtonData.Usages = (PUSAGE)
             calloc (data->ButtonData.MaxUsageLength, sizeof (USAGE));

        data->ReportID = buttonCaps -> ReportID;
    }

    for (i = 0; i < numValues; i++, valueCaps++) 
    {
        if (valueCaps->IsRange)
        {
            for (usage = valueCaps->Range.UsageMin;
                 usage <= valueCaps->Range.UsageMax;
                 usage++)
            {
                data->IsButtonData = FALSE;
                data->Status = HIDP_STATUS_SUCCESS;
                data->UsagePage = valueCaps->UsagePage;
                data->ValueData.Usage = usage;
                data->ReportID = valueCaps -> ReportID;
                data++;
            }
        } 
        else
        {
            data->IsButtonData = FALSE;
            data->Status = HIDP_STATUS_SUCCESS;
            data->UsagePage = valueCaps->UsagePage;
            data->ValueData.Usage = valueCaps->NotRange.Usage;
            data->ReportID = valueCaps -> ReportID;
            data++;
        }
    }

    return;
}


VOID
CloseHidDevice (
    IN PHID_DEVICE HidDevice,
    IN BOOL        FreeDeviceInfo
)
{
	if(HidDevice->bFound) {
		HidDevice->bFound = FALSE;
	//	if(HidDevice->hDeviceNotificationHandle) {
	//		UnregisterDeviceNotification(HidDevice->hDeviceNotificationHandle);
	//		HidDevice->hDeviceNotificationHandle = NULL;
	//	}

		free(HidDevice -> DevicePath);

		if (INVALID_HANDLE_VALUE != HidDevice -> HidDevice)
		{
			CloseHandle(HidDevice -> HidDevice);
		}

		//
		// Only free these structure, if have a handle to an non-overlapped device
		//

		if (FreeDeviceInfo) 
		{
			if (NULL != HidDevice -> Ppd)
			{
				HidD_FreePreparsedData(HidDevice -> Ppd);
			}

			if (NULL != HidDevice -> InputReportBuffer)
			{
				free(HidDevice -> InputReportBuffer);
			}

			if (NULL != HidDevice -> InputData)
			{
				free(HidDevice -> InputData);
			}

			if (NULL != HidDevice -> InputButtonCaps)
			{
				free(HidDevice -> InputButtonCaps);
			}

			if (NULL != HidDevice -> InputValueCaps)
			{
				free(HidDevice -> InputValueCaps);
			}

			if (NULL != HidDevice -> OutputReportBuffer)
			{
				free(HidDevice -> OutputReportBuffer);
			}

			if (NULL != HidDevice -> OutputData)
			{
				free(HidDevice -> OutputData);
			}

			if (NULL != HidDevice -> OutputButtonCaps) 
			{
				free(HidDevice -> OutputButtonCaps);
			}

			if (NULL != HidDevice -> OutputValueCaps)
			{
				free(HidDevice -> OutputValueCaps);
			}

			if (NULL != HidDevice -> FeatureReportBuffer)
			{
				free(HidDevice -> FeatureReportBuffer);
			}

			if (NULL != HidDevice -> FeatureData) 
			{
				free(HidDevice -> FeatureData);
			}

			if (NULL != HidDevice -> FeatureButtonCaps) 
			{
				free(HidDevice -> FeatureButtonCaps);
			}

			if (NULL != HidDevice -> FeatureValueCaps) 
			{
				free(HidDevice -> FeatureValueCaps);
			}
		}        
	}
    return;
}


